﻿using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using Aspose.Slides;
using Aspose.Slides.Animation;

namespace Helpers
{
    public static class PPTHelper
    {

        /// <summary> 获取 PPT 文档单页中的Shape，此项为动画的触发项 </summary>
        /// <param name="ieffect"></param>
        /// <returns></returns>
        private static AutoShape GetAutoShape(IEffect ieffect)
        {
            AutoShape autoshape = null;
            var effect = ieffect as Effect;
            var flag = BindingFlags.Instance | BindingFlags.NonPublic;
            // 在 IEffect 中，PPTXUnsupportedProps 是获取触发项的属性
            var props = effect.TextAnimation.GetType().GetProperty("PPTXUnsupportedProps", flag);
            if (props != null)
            {
                var animation = props.GetValue(effect.TextAnimation, null);
                // 由于 dll 混淆后，无法直接获取属性的名称且触发项被设置为了保护项，因此需要反射 ishape_0 获取
                var ishapefield = animation.GetType().GetField("ishape_0", flag);
                var ishape = ishapefield.GetValue(animation);
                autoshape = ishape as AutoShape;
            }
            return autoshape;
        }

        /// <summary> 设置默认的 Slide 显示内容，在有动画的情况下， ISlide 显示的是编辑状态下最终呈现的，因此要修改其属性为动画播放状态初始界面 </summary>
        /// <param name="islide"></param>
        /// <returns></returns>
        private static bool SetDefaultSlide(ISlide islide)
        {
            // ISlide 中的 Timeline.MainSequence 记录的是动画项
            var slidecount = islide.Timeline.MainSequence.Count;
            if (slidecount == 0)
            {
                return false;
            }

            for (int i = 0; i < slidecount; i++)
            {
                var ieffect = islide.Timeline.MainSequence[i];
                var autoshape = GetAutoShape(ieffect);
                foreach (var behavior in ieffect.Behaviors)
                {
                    // Behaviors 中暂时只需要判断 SetEffect 项，因为其他项设置的是缓动和位置，因此无需处理
                    var seteffect = behavior as SetEffect;
                    if (seteffect != null)
                    {
                        //因为在此有动画设置此项为可见，反推之，此项默认应该隐藏
                        if (seteffect.To.ToString() == "visible")
                        {
                            autoshape.Hidden = true;
                        }
                        continue;
                    }
                }
            }
            return true;
        }

        public static List<string> ConvertToImages(string pptfile, string savedirectory = "")
        {
            // 返回的图片绝对路径集合
            List<string> images = new List<string>();

            // 获取 Word 文件名称
            var pptname = System.IO.Path.GetFileNameWithoutExtension(pptfile);

            // 如果指定了保存路径，则使用传入的路径，否则在 Pdf 文件的同级创建同名文件夹当作保存路径
            savedirectory = string.IsNullOrWhiteSpace(savedirectory) ? System.IO.Path.GetDirectoryName(pptfile) : savedirectory;
            savedirectory = System.IO.Path.Combine(savedirectory, pptname);
            // 防止保存的路径不存在因此创建保存文件夹
            Directory.CreateDirectory(savedirectory);

            var pres = new Presentation(pptfile);

            int pptcount = pres.Slides.Count;
            for (int p = 0; p < pptcount; p++)
            {
                var item = pres.Slides[p];
                var imagefile = System.IO.Path.Combine(savedirectory, p + ".jpg");

                if (!SetDefaultSlide(item))
                {
                    // 获取 2 倍的图像，用以解决 PPT 内容过小时分辨率过小 ，下同
                    item.GetThumbnail(2.0f, 2.0f).Save(imagefile);
                    images.Add(imagefile);

                    continue;
                }
                imagefile = System.IO.Path.Combine(savedirectory, p + "_0.jpg");
                item.GetThumbnail(2.0f, 2.0f).Save(imagefile);
                images.Add(imagefile);

                // 在此每一个动画需要截图一次，因为每一个动画都有自己的显示内容
                var slidecount = item.Timeline.MainSequence.Count;
                for (int i = 0; i < slidecount; i++)
                {
                    var ieffect = item.Timeline.MainSequence[i];
                    var autoshape = GetAutoShape(ieffect);
                    try
                    {
                        foreach (var behavior in ieffect.Behaviors)
                        {
                            var seteffect = behavior as SetEffect;
                            if (seteffect != null)
                            {
                                //由于此属性标识当前项可见，于是需要还原状态用以截图
                                autoshape.Hidden = seteffect.To.ToString() != "visible";
                                continue;
                            }
                        }
                    }
                    catch { }
                    imagefile = System.IO.Path.Combine(savedirectory, p + "_" + i + ".jpg");
                    item.GetThumbnail(2.0f, 2.0f).Save(imagefile);
                    images.Add(imagefile);
                }
            }

            return images;
        }

        /// <summary> 获取 PPT 中包含动画的所有可显示的 Bitmap </summary>
        /// <param name="path"> PPT 文件路径 ，可以为 .pptx .ppt .pptm </param>
        /// <returns> 包含了所有动画页的图像 </returns>
        public static List<Bitmap> GetPPTImages(string path)
        {
            var bmps = new List<Bitmap>();
            var pres = new Presentation(path);
            foreach (var item in pres.Slides)
            {
                if (!SetDefaultSlide(item))
                {
                    // 获取 2 倍的图像，用以解决 PPT 内容过小时分辨率过小 ，下同
                    bmps.Add(item.GetThumbnail(2.0f, 2.0f));
                    continue;
                }

                bmps.Add(item.GetThumbnail(2.0f, 2.0f));

                // 在此每一个动画需要截图一次，因为每一个动画都有自己的显示内容
                var slidecount = item.Timeline.MainSequence.Count;
                for (int i = 0; i < slidecount; i++)
                {
                    var ieffect = item.Timeline.MainSequence[i];
                    var autoshape = GetAutoShape(ieffect);
                    try
                    {
                        foreach (var behavior in ieffect.Behaviors)
                        {
                            var seteffect = behavior as SetEffect;
                            if (seteffect != null)
                            {
                                //由于此属性标识当前项可见，于是需要还原状态用以截图
                                autoshape.Hidden = seteffect.To.ToString() != "visible";
                                continue;
                            }
                        }
                    }
                    catch { }

                    bmps.Add(item.GetThumbnail(2.0f, 2.0f));
                }
            }

            return bmps;
        }

    }
}
